from dataclasses import dataclass
from datetime import datetime
from typing import List, Union, Type, Tuple

from emi.bar.data import BarData
from pyecharts import charts
import pyecharts.options as opts

from emiui.chart import _KChart, KChart
from emiui.chart._KChart import KChart_Main


@dataclass
class KModel:
    KDJ = _KChart.KDJ()  ## kdj图
    MACD = _KChart.MACD()

    CHART_BOTTOM1 = _KChart.KChart_Vol()
    CHART_BOTTOM2 = _KChart.ADOSC()  ## 默认底部看板


    is_daily:bool  ## 是否日频率级别数据

    @dataclass
    class KItem:
        """
        k线值
        """
        time:datetime
        vol:int
        amount:float
        k_value:List[float]  # [open, close, low, high,pre_close]  # k值
        bar:BarData


    k_max_value:float = None  ## 最大可显示范围
    k_min_value:float = None  ## 最小可显示范围
    k_items:List[KItem] = None

    highlight_area:Tuple[datetime,datetime,str] = None   ## 高亮度区域：[start,end,name]

    def __post_init__(self):
        self.reset()

    def reset(self):
        self.k_items = []
        self.k_min_value = None
        self.k_max_value = None

    def set_bar_list(self,bars:List[BarData]):
        self.reset()
        for bar in bars:
            self.add_bar(bar)

    def add_bar(self,bar:BarData):
        pre_k_item = self.k_items[-1] if self.k_items else None
        k_item = self._make_k_item(bar)
        if pre_k_item:
            assert pre_k_item.time <= k_item.time  ## 保证时间顺序
        k_min = k_item.k_value[2] * 0.99
        k_max = k_item.k_value[3] * 1.01
        if self.k_max_value is None or self.k_max_value < k_max:
            self.k_max_value = k_max
        if self.k_min_value is None or self.k_min_value > k_min:
            self.k_min_value = k_min
        self.k_items.append(k_item)

    def _make_k_item(self,bar:BarData)->KItem:
        k_item = KModel.KItem(
            time=bar.datetime,
            vol=int(bar.volume),
            amount=bar.volume,
            k_value=[bar.open,bar.close,bar.low,bar.high,bar.pre_close],
            bar=bar
        )
        return k_item

    def market_x_value(self,time:datetime):
        return f"{time.date()}" if self.is_daily else f"{time.strftime('%Y/%m/%d %H:%M')}"

    def build_chart(self,is_line=False, title:str="",
                   main:Union[KChart,Type[KChart]] = None,
                   bottom1:Union[KChart,Type[KChart]] = None,
                   bottom2:Union[KChart,Type[KChart]]= CHART_BOTTOM2):
        """
        显示k线图表
        :param is_line: 是k线还是k图
        :param title: 标题
        :param content: main主要内容chart：默认是k线图或者K图
        :param bottom1: 底部chart，默认是成交量图
        :param bottom2:
        :return:
        """

        x_list = [ self.market_x_value(k_item.time) for k_item in self.k_items]  ### x轴的时间值
        k_bar_values = [k_item.k_value for k_item in self.k_items]

        param = {}

        main_chart_factory:KChart = KChart_Main() if main is None else (main if isinstance(main,KChart) else main())
        bottom1_chart_factory:KChart = KModel.CHART_BOTTOM1 if bottom1 is None else (bottom1 if isinstance(bottom1,KChart) else bottom1())
        bottom2_chart_factory:KChart = None if bottom2 is None else bottom2 if isinstance(bottom2,KChart) else bottom2()

        chart_size = ""
        param['chart_size'] =  3 if bottom2_chart_factory else 2

        chart_top = main_chart_factory.make_chart(self,is_line=is_line,title=title,x_list=x_list,**param)
        chart_bottom1= bottom1_chart_factory.make_chart(self,is_line=is_line,title=title,x_list=x_list,**param)
        chart_bottom2 = bottom2_chart_factory.make_chart(self,is_line=is_line,title=title,x_list=x_list,**param) if bottom2_chart_factory else None

        if chart_bottom2 is None:
            ### 需要预留10%给底部滚动条，所以加起来的和要等于90
            main_height = 45
            main_botton_margin = 15
            bottom_1_height = 30
            bottom_2_height = 0
        else:
            main_height = 36
            main_botton_margin = 10
            bottom_1_height = 22
            bottom_2_height = 22



        grid_chart = charts.Grid(
            init_opts=opts.InitOpts(
                width="100%",
                height="600px",
                animation_opts=opts.AnimationOpts(animation=False),
            )
        )

        # 这个是为了把 data.datas 这个数据写入到 html 中,还没想到怎么跨 series 传值
        # demo 中的代码也是用全局变量传的
        grid_chart.add_js_funcs("var barData = {}".format(k_bar_values))

        grid_chart.add(
            chart_top,
            grid_opts=opts.GridOpts(pos_left="5%", pos_right="8%", height=f"{main_height}%"),
        )

        grid_chart.add(
            chart_bottom1,
            grid_opts=opts.GridOpts(
                pos_left="5%", pos_right="8%", pos_top=f"{main_height+main_botton_margin}%", height=f"{bottom_1_height}%"
            ),
        )
        if chart_bottom2:
            grid_chart.add(
                chart_bottom2,
                grid_opts=opts.GridOpts(
                    pos_left="5%", pos_right="8%", pos_top=f"{main_height + main_botton_margin + bottom_1_height}%",
                    height=f"{bottom_2_height}%"
                ),
            )
        return grid_chart

